﻿/*-----------------------------------------------
// File: BattleViewCtrl.cs 
// Description: 战场渲染层，总控制
// Author: Shaobing	492057342@qq.com
-----------------------------------------------*/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using ProtoBuf.Message;
using AssetLoad;
using UnityEngine.SceneManagement;
using System;

namespace Battle.View
{
    /// <summary>
    /// 战场渲染层总控
    /// </summary>
    public class BattleViewCtrl : Fsm.FsmControllerBaseMono<BattleState>
    {
        BattleManager manager;
        public Camera battleCamera
        {
            get { return BattleCamera.Instance.battleCamera; }
        }
        public GameObject changeFormationEffect;
        #region Scene
        public GameObject SceneObj
        {
            get
            {
                if(sceneObj == null)
                {
                    sceneObj = GameObject.Find("/" + SceneManager.GetActiveScene().name);
                }
                return sceneObj;
            }
        }

        public void ShowSceneObj() { SceneObj.SetActive(true); }
        public void HideSceneObj() { SceneObj.SetActive(false); }
        private GameObject sceneObj = null;
        #endregion

        #region 回合开始结束的期望
        public List<OnceAttackPackageBase> roundBeginExports = new List<OnceAttackPackageBase>();
        public List<OnceAttackPackageBase> roundEndExports = new List<OnceAttackPackageBase>();
        #endregion

        #region 当前回合战斗行为Cache（大招）
        UltimateBehaviourPackage ultimateBehaviour;
        public void EnqueueUltimateBehaviour(string uid, List<string> targetUids)
        {
            ultimateBehaviour = new UltimateBehaviourPackage(uid, targetUids);
            manager.battlePanel.ReceiveSkillCastMessage(uid, SkillType.Ultimate);
        }
        public bool IsHaveUltimateBehaviour()
        {
            return ultimateBehaviour != null;
        }
        public UltimateBehaviourPackage GetUltimatePackage() { return ultimateBehaviour; }
        public void DoneUltimateBehaviour()
        {
            if (ultimateBehaviour != null)
            {
                var bc = GetBattleCharacter(ultimateBehaviour.actorUid);
                if (bc != null)
                {
                    var skill = bc.GetSkill(SkillType.Ultimate);
                    if (skill != null)
                    {
                        skill.Done();
                    }
                }
            }
            ultimateBehaviour = null;
        }

        public bool isUltimateBeginCast = false;
        #endregion

        #region 当前回合战斗行为Cache(普通攻击)
        // 一次战斗行为，包含:向施法坐标点移动-》攻击
        /// <summary>
        /// 战斗行为队列
        /// </summary>
        Queue<OnceBehaviourPackage> behaviourQueue = new Queue<OnceBehaviourPackage>();
        /// <summary>
        /// 等待执行完毕的战斗行为队列
        /// </summary>
        List<OnceBehaviourPackage> currentBehaviours = new List<OnceBehaviourPackage>();
        /// <summary>
        /// 已完成的战斗行为队列
        /// </summary>
        List<OnceBehaviourPackage> doneBehaviours = new List<OnceBehaviourPackage>();

        public OnceBehaviourPackage GetCurrentBehaviour(string uid, SkillType t)
        {
            foreach (var beh in currentBehaviours)
            {
                if (beh.actorUid == uid && beh.skillType == t)
                {
                    return beh;
                }
            }
            return null;
        }
        public void EnqueueBehaviour(string uid, SkillType t, int FPId, BattleCamp camp, int needMove)
        {

            behaviourQueue.Enqueue(new OnceBehaviourPackage(uid, t, FPId, camp, needMove));
        }
        public bool IsHaveBehaviourNeedDo()
        {
            return behaviourQueue.Count > 0;
        }
        public OnceBehaviourPackage DequeueBehaviour()
        {
            if (behaviourQueue.Count > 0)
                return behaviourQueue.Dequeue();
            return null;
        }
        public void DoBehaviourPackage(OnceBehaviourPackage package)
        {
            //            Debug.Log("[DoBehaviourPackage] DoPackage UID:" + package.actorUid + " SkillType:" + package.skillType.ToString());
            currentBehaviours.Add(package);
        }
        public void DoneBehaviourPackage(OnceBehaviourPackage package)
        {
            //            Debug.Log("[DoneBehaviourPackage] DonePackage UID:" + package.actorUid + " SkillType:" + package.skillType.ToString());
            currentBehaviours.Remove(package);
            doneBehaviours.Add(package);
        }

        public bool IsBehaviourDone(OnceBehaviourPackage package)
        {
            return doneBehaviours.Contains(package);
        }

        public bool IsHaveRemoteBehaviour()
        {
            foreach (var behaviour in currentBehaviours)
            {
                var bc = GetBattleCharacter(behaviour.actorUid);
                var skill = bc.GetSkill(behaviour.skillType);
                if (skill != null && skill.attackCameraRule == AttackCameraRule.Remote)
                {
                    return true;
                }
            }
            return false;
        }
        public bool IsCurrentBehaviourDoOver()
        {
            if (behaviourQueue.Count == 0 && currentBehaviours.Count == 0)
            {
                return true;
            }
            return false;
        }

        public void ClearCurrentBehaivour()
        {
            ultimateBehaviour = null;
            currentBehaviours.Clear();
            behaviourQueue.Clear();
            doneBehaviours.Clear();
        }
        #endregion

        #region 战场实例
        #region BattleCharacter
        Dictionary<string, BattleCharacter> allActors = new Dictionary<string, BattleCharacter>();
        public Dictionary<string, BattleCharacter> GetAllActors()
        {
            return allActors;
        }
        List<string> attackerUids = new List<string>();
        List<string> defenderUids = new List<string>();
        public List<string> GetActorUids(BattleCamp camp)
        {
            if (camp == BattleCamp.Attacker)
                return attackerUids;
            else
                return defenderUids;
        }
        public BattleCharacter GetBattleCharacter(string uid)
        {
            if (allActors.ContainsKey(uid))
                return allActors[uid];
            return null;
        }
        public BattleCharacter GetBattleCharacter(BattleCamp camp, int formationIndex)
        {
            foreach (var kvp in allActors)
            {
                if (kvp.Value.camp == camp && kvp.Value.FPId == formationIndex)
                    return kvp.Value;
            }
            return null;
        }

        public BattleCharacter GetBattleCharacter(BattlePos pos)
        {
            return GetBattleCharacter(pos.camp, pos.FPId);
        }

        public ProtoBuf.Message.BattlePlayer GetMyBattlePlayer()
        {
            ProtoBuf.Message.BattlePlayer player = new BattlePlayer();

            return player;
        }

        public string attackerWaveUid;
        public string defenderWaveUid;
        public BattleCamp GetCamp(string fighterUID)
        {
            if (attackerWaveUid == fighterUID)
                return BattleCamp.Attacker;
            else if (defenderWaveUid == fighterUID)
                return BattleCamp.Defender;
            else
            {
                Debug.LogWarning("[BattleViewCtrl GetCamp] fighterUID Not Exist!!!!");
                return BattleCamp.Attacker;
            }
        }


        public bool isAttackerPlayer;
        public bool isDefenderPlayer;

        public void BuildActorFromCache()
        {
            List<string> needRemoveBCs = new List<string>();
            foreach (var actor in allActors)
            {
                if (attackerInitData.GetBattleActorData(actor.Key) != null || defenderInitData.GetBattleActorData(actor.Key) != null)
                {

                }
                else
                {
                    needRemoveBCs.Add(actor.Key);
                }
            }
            foreach (var id in needRemoveBCs)
            {
                allActors[id].UnloadResource();
                Destroy(allActors[id].gameObject);
                allActors.Remove(id);
            }
            foreach (var actor in attackerInitData.actors)
            {
                if (allActors.ContainsKey(actor.uniqueId))
                {
                    allActors[actor.uniqueId].Refresh(actor);
                }
                else
                {
                    var bc = BattleFactory.CreateBattleCharacter();
                    bc.Init(actor, BattleCamp.Attacker);
                    allActors.Add(actor.uniqueId, bc);
                }
            }
            foreach (var actor in defenderInitData.actors)
            {
                if (allActors.ContainsKey(actor.uniqueId))
                {
                    allActors[actor.uniqueId].Refresh(actor);
                }
                else
                {
                    var bc = BattleFactory.CreateBattleCharacter();
                    bc.Init(actor, BattleCamp.Defender);
                    allActors.Add(actor.uniqueId, bc);
                }
            }
            attackerUids.Clear();
            defenderUids.Clear();
            foreach (var bc in allActors)
            {
                if (bc.Value.camp == BattleCamp.Attacker)
                    attackerUids.Add(bc.Key);
                else
                    defenderUids.Add(bc.Key);
            }
        }
        void RefreshSideBattleData(OneFighter fighter, BattleCamp camp)
        {
            SetMaxAngerNum(fighter.maxAngerNum, camp);
            SetAngerNum(fighter.angerNum, camp);

            foreach (var actor in fighter.actors)
            {
                var bc = GetBattleCharacter(actor.uid);
                if (bc != null)
                {
                    bc.Refresh(actor);
                }
            }
        }

        #endregion

        #region UltimateLogic
        public class UltimateLogicCounter
        {
            public UltimateLogic logic;
            public int count;
            public bool isLoadComplete;
            public void LoadComplete(UltimateLogic l)
            {
                logic = l;
                isLoadComplete = true;
            }
            public UltimateLogicCounter(UltimateLogic l, int count = 1)
            {
                logic = l;
                this.count = count;
                isLoadComplete = true;
            }
            public UltimateLogicCounter(int count = 1)
            {
                logic = null;
                count = 1;
                isLoadComplete = false;
            }
        }
        Dictionary<string, UltimateLogicCounter> ultimateObjs = new Dictionary<string, UltimateLogicCounter>();
        public void AddUltimateLogic(string hsId)
        {
            if (ultimateObjs.ContainsKey(hsId))
                ultimateObjs[hsId].count++;
            else
            {
                ultimateObjs[hsId] = new UltimateLogicCounter();
            }
        }
        public void CompleteUltimateLogic(string hsId, UltimateLogic logic)
        {
            if (ultimateObjs.ContainsKey(hsId) && !ultimateObjs[hsId].isLoadComplete)
            {
                ultimateObjs[hsId].LoadComplete(logic);
                logic.transform.SetParent(transform);
                logic.transform.localPosition = Vector3.zero;
                logic.transform.localEulerAngles = Vector3.zero;
                logic.transform.localScale = Vector3.one;
            }
        }
        public void AddUltimateLogic(string hsId, UltimateLogic logic)
        {
            if(logic == null)
            {
                return;
            }
            if (ultimateObjs.ContainsKey(hsId))
            {
                ultimateObjs[hsId].count++;
            }
            else
            {
                ultimateObjs[hsId] = new UltimateLogicCounter(logic);
                logic.transform.SetParent(transform);
                logic.transform.localPosition = Vector3.zero;
                logic.transform.localEulerAngles = Vector3.zero;
                logic.transform.localScale = Vector3.one;
            }
        }
        /// <summary>
        /// 卸载UltimateLogic
        /// </summary>
        /// <param name="hsId"></param>
        /// <returns>True: 需要卸载 False: 不需要卸载</returns>
        public void RemoveUltimateLogic(string hsId, string resourceName)
        {
            if (ultimateObjs.ContainsKey(hsId))
            {
                ultimateObjs[hsId].count--;
                if (ultimateObjs[hsId].count <= 0)
                {
                    GameObject.Destroy(ultimateObjs[hsId].logic.gameObject);
                    AssetLoad.AssetBundleManager.Instance.UnloadAssetBundle("ultimate/"+resourceName);
                    //ResourceLoader.GetInstance().ultiLoader.UnloadUlti(ultimateObjs[hsId].logic.gameObject, resourceName);
                    ultimateObjs.Remove(hsId);
                }
            }
        }
        public bool IsHaveUltimateLogic(string HSId)
        {
            return ultimateObjs.ContainsKey(HSId);
        }
        public UltimateLogic GetUltimateLogic(string HSId)
        {
            if (ultimateObjs.ContainsKey(HSId))
                return ultimateObjs[HSId].logic;
            return null;
        }
        #endregion

        #region Projectile

        /// <summary>
        /// 抛射物
        /// </summary>
        //List<BattleProjectile> allProjectiles = new List<BattleProjectile>();

        List<Projectile> allProjectiles = new List<Projectile>();
        public void ShootProjectile(ProjectilePacket pp)
        {
            var bp = ProjectileFactory.CreateProjectile(pp);
            bp.Run();
            allProjectiles.Add(bp);
        }
        public void UnSpawnProjectile(Projectile bp)
        {
            if (allProjectiles.Contains(bp))
                allProjectiles.Remove(bp);
        }
        #endregion

        #region Buff
        public BufferResourceCache bufferResourceCache;

        Dictionary<string, BattleBuffer> allBuffs = new Dictionary<string, BattleBuffer>();
        public BattleBuffer GetBattleBuff(string buffUid)
        {
            if (allBuffs.ContainsKey(buffUid))
                return allBuffs[buffUid];
            return null;
        }

        public void AddBattleBuff(ProtoBuf.Message.Buffer buff)
        {
            BattleBuffer battleBuff = new BattleBuffer();
            battleBuff.Init(buff);
            battleBuff.Run();
            allBuffs[buff.uid] = battleBuff;

        }

        public void RemoveBattleBuff(string uniqueId)
        {
            if (allBuffs.ContainsKey(uniqueId))
            {
                allBuffs[uniqueId].Stop();
                allBuffs.Remove(uniqueId);
            }
        }
        #endregion


        #endregion
        #region 战场状态信息
        int roundNum;               // 当前回合数
        public int GetRoundNum() { return roundNum; }
        double attackerAngerNum;       // 攻击方怒气值
        double attackerMaxAngerNum;
        double defenderAngerNum;       // 防守方怒气值
        double defenderMaxAngerNum;
        public double GetAngerNum(BattleCamp camp)
        {
            if (camp == BattleCamp.Attacker)
                return attackerAngerNum;
            else
                return defenderAngerNum;
        }
        public void SetAngerNum(double v, BattleCamp camp)
        {
            if (camp == BattleCamp.Attacker)
                attackerAngerNum = v;
            else
                defenderAngerNum = v;
        }
        public double GetAngerMax(BattleCamp camp)
        {
            if (camp == BattleCamp.Attacker)
                return attackerMaxAngerNum;
            else
                return defenderMaxAngerNum;
        }
        public void AddAnger(double addValue, BattleCamp camp)
        {
            if (camp == BattleCamp.Attacker)
            {
                attackerAngerNum += addValue;
                if (attackerAngerNum >= attackerMaxAngerNum)
                {
                    attackerAngerNum = attackerMaxAngerNum;
                }
                else if (attackerAngerNum < 0)
                {
                    attackerAngerNum = 0;
                }
            }
            else
            {
                defenderAngerNum += addValue;
                if (defenderAngerNum >= defenderMaxAngerNum)
                {
                    defenderAngerNum = defenderMaxAngerNum;
                }
                else if (defenderAngerNum < 0)
                {
                    defenderAngerNum = 0;
                }
            }
        }

        public void SetMaxAngerNum(double v, BattleCamp camp)
        {
            if (camp == BattleCamp.Attacker)
                attackerMaxAngerNum = v;
            else
                defenderMaxAngerNum = v;
        }

        public bool IsAngerMax(BattleCamp camp)
        {
            if (camp == BattleCamp.Attacker)
                return attackerAngerNum >= attackerMaxAngerNum;
            else
                return defenderAngerNum >= defenderMaxAngerNum;
        }

        public BattleCamp curRunningCamp;

        public bool isFirstAttack = false;

        public bool isAuto = false;


        #endregion
        #region 战场初始信息
        public int maxRoundNum { get; protected set; }
        public bool hasRoundFight = false;
        public bool isFormationTransfered = false;

        public BattleCamp initRunCamp { get; protected set; }     // 初始行动阵营

        BattleFighterData attackerInitData;
        BattleFighterData defenderInitData;

        public void InitBattleData(int maxRound, BattleFighterData attacker, BattleFighterData defender)
        {
            this.maxRoundNum = maxRound;
            attackerInitData = attacker;
            defenderInitData = defender;
            isAuto = attacker.isAuto;

            if (StaticData.curBattleData.battleType == BattleType.PVE && StaticData.IsFirstChapter1_1())
            {
                this.isTipGuideNeed = true;
                BattleManager.Instance.battlePanel.gameObject.AddMissingComponent<Battle.BattleHeroItemGudieCtrl>();
            }
            else
            {
                this.isTipGuideNeed = false;
            }
        }

        public void InitBattleData(Fighter attacker, Fighter defender)
        {
            /*
            if (attacker.isAttack == 0 && defender.isAttack != 0)
                initRunCamp = BattleCamp.Defender;
            else if (attacker.isAttack != 0 && defender.isAttack == 0)
                initRunCamp = BattleCamp.Attacker;
            else
            {
                Debug.LogError("Init Battle Data, isAttack Value Error. Attacker:" + attacker.isAttack + " Defender:" + defender.isAttack);
                initRunCamp = BattleCamp.Attacker;
            }
            SetMaxAngerNum(attacker.maxAngerNum, BattleCamp.Attacker);
            SetAngerNum(attacker.angerNum, BattleCamp.Attacker);
            SetMaxAngerNum(defender.maxAngerNum, BattleCamp.Defender);
            SetAngerNum(defender.angerNum, BattleCamp.Defender);
            attackerWaveUid = attacker.fighterUid;
            defenderWaveUid = defender.fighterUid;
            isAttackerPlayer = attacker.isPlayer == 0 ? false : true;
            isDefenderPlayer = defender.isPlayer == 0 ? false : true;

            attackerRoleDataCache.Clear();
            defenderRoleDataCache.Clear();
            attackerActorCache.Clear();
            defenderActorCache.Clear();
            foreach (var roleActor in attacker.roleActors)
            {
                attackerRoleDataCache[roleActor.role.pid] = new RoleData(roleActor.role);
                foreach (var actor in roleActor.actors)
                {
                    attackerActorCache[actor.uid] = new ActorData(actor);
                }
            }
            foreach (var roleActor in defender.roleActors)
            {
                defenderRoleDataCache[roleActor.role.pid] = new RoleData(roleActor.role);
                foreach (var actor in roleActor.actors)
                {
                    defenderActorCache[actor.uid] = new ActorData(actor);
                }
            }
            */
        }
        #endregion

        #region 站位信息
        public float GetForwardPositionZ(BattleCamp camp)
        {
            if (camp == BattleCamp.Attacker)
            {
                return positionOffset - blockZ * 1.5f;
            }
            else
            {
                return -positionOffset + blockZ * 1.5f;
            }
        }
        public float positionOffset
        {
            get { return (float)Dict.DictDataManager.Instance.tableGameConfigureDao.currentData.FormationCenterOffset; }
        }

        public const int rowNum = 3;
        public const int colNum = 5;
        public float blockX
        {
            get
            {
                return (float)Dict.DictDataManager.Instance.tableGameConfigureDao.currentData.FormationBlockX;
            }
        }
        public float blockZ
        {
            get
            {
                return (float)Dict.DictDataManager.Instance.tableGameConfigureDao.currentData.FormationBlockZ;
            }
        }

        public Vector3 GetPosition(BattlePos pos)
        {
            return GetPosition(pos.FPId, pos.camp);
        }

        /// <summary>
        /// 获得站位实际坐标
        /// </summary>
        /// <param name="index"></param>
        /// <param name="camp"></param>
        /// <returns></returns>
        public Vector3 GetPosition(int FPId, BattleCamp camp)
        {
            if (camp == BattleCamp.Attacker && isAttackerPlayer)
            {
                if (FPId == 3)
                {
                    var pos = TableConvertUtil.String2Vector3(Dict.DictDataManager.Instance.tableGameConfigureDao.currentData.FormationPlayer3Pos);
                    return pos + new Vector3(0, 0, -positionOffset);
                }
                else if (FPId == 7)
                {
                    var pos = TableConvertUtil.String2Vector3(Dict.DictDataManager.Instance.tableGameConfigureDao.currentData.FormationPlayer7Pos);
                    return pos + new Vector3(0, 0, -positionOffset);
                }
                else if (FPId == 9)
                {
                    var pos = TableConvertUtil.String2Vector3(Dict.DictDataManager.Instance.tableGameConfigureDao.currentData.FormationPlayer9Pos);
                    return pos + new Vector3(0, 0, -positionOffset);
                }
                else if (FPId == 13)
                {
                    var pos = TableConvertUtil.String2Vector3(Dict.DictDataManager.Instance.tableGameConfigureDao.currentData.FormationPlayer13Pos);
                    return pos + new Vector3(0, 0, -positionOffset);
                }
                else
                {
                    Debug.LogError("[GetPosition] Player At Wrong FPId: " + FPId);
                    return Vector3.zero;
                }
            }
            else if (camp == BattleCamp.Defender && isDefenderPlayer)
            {
                if (FPId == 3)
                {
                    var pos = TableConvertUtil.String2Vector3(Dict.DictDataManager.Instance.tableGameConfigureDao.currentData.FormationPlayer3Pos);
                    return new Vector3(pos.x, pos.y, -pos.z) + new Vector3(0, 0, positionOffset);
                }
                else if (FPId == 7)
                {
                    var pos = TableConvertUtil.String2Vector3(Dict.DictDataManager.Instance.tableGameConfigureDao.currentData.FormationPlayer7Pos);
                    return new Vector3(pos.x, pos.y, -pos.z) + new Vector3(0, 0, positionOffset);
                }
                else if (FPId == 9)
                {
                    var pos = TableConvertUtil.String2Vector3(Dict.DictDataManager.Instance.tableGameConfigureDao.currentData.FormationPlayer9Pos);
                    return new Vector3(pos.x, pos.y, -pos.z) + new Vector3(0, 0, positionOffset);
                }
                else if (FPId == 13)
                {
                    var pos = TableConvertUtil.String2Vector3(Dict.DictDataManager.Instance.tableGameConfigureDao.currentData.FormationPlayer13Pos);
                    return new Vector3(pos.x, pos.y, -pos.z) + new Vector3(0, 0, positionOffset);
                }
                else
                {
                    Debug.LogError("[GetPosition] Player At Wrong FPId: " + FPId);
                    return Vector3.zero;
                }
            }
            else
            {
                int row = (FPId - 1) / colNum + 1;
                int col = (FPId - 1) % colNum + 1;
                if (camp == BattleCamp.Attacker)
                {

                    return new Vector3((col - 0.5f) * blockX - colNum * blockZ * 0.5f, 0f, -(row - 0.5f) * blockZ + rowNum * blockZ * 0.5f - positionOffset);
                }
                else
                {
                    return new Vector3((col - 0.5f) * blockX - colNum * blockZ * 0.5f, 0f, (row - 0.5f) * blockZ - rowNum * blockZ * 0.5f + positionOffset);
                }
            }

        }

        #endregion

        #region Resource
        public bool IsSceneLoaded;
        public bool IsBattleBaseLoaded = false;
        protected string hztxPath = "particles/UI_DXP/UI_huanzhen";
        public virtual IEnumerator LoadBase()
        {
            hztxPath = "particles/"+Dict.Blo.DictParticleBlo.GetCommonParticle("12").ResourcePath;
            LoadBuffTransform();
            yield return StartCoroutine(BattleFactory.RegistCommon());
            LoadShadow();
            
            AssetBundleManager.Instance.LoadAssetBundle(hztxPath);
            while(!AssetBundleManager.Instance.IsLoadedAssetBundle(hztxPath))
            {
                yield return 1;
            }
            changeFormationEffect = Instantiate(AssetBundleManager.Instance.GetAssetFromBundle(hztxPath,"UI_huanzhen",typeof(GameObject))) as GameObject;
            changeFormationEffect.transform.localPosition = Vector3.zero;
            changeFormationEffect.SetActive(false);
            CommonMethod.ChangeLayer(changeFormationEffect.transform, LayerName.SceneParticle);

            bufferResourceCache = BufferResourceCache.Initialize();
            yield return StartCoroutine(AudioManager.Instance.LoadAudioResource("SE_dead"));

            IsBattleBaseLoaded = true;
        }

        public virtual void UnloadBase()
        {
            GameObject.Destroy(changeFormationEffect);
            changeFormationEffect = null;
            AssetBundleManager.Instance.UnloadAssetBundle(hztxPath);
            UnloadShadow();
            UnloadBuffTransform();
            BattleFactory.UnRegistCommon();
            AudioManager.Instance.UnloadAudioResource("SE_dead");
            BufferResourceCache.UnInitialize(bufferResourceCache);
            IsBattleBaseLoaded = false;
        }

        public void Unload()
        {
            try
            {
                foreach (var buf in allBuffs)
                {
                    buf.Value.Stop();
                }
            }
            catch (Exception e)
            {
                Debug.LogError("Unload buffs Exception: " + e.ToString());
            }
            finally
            {
                allBuffs.Clear();
            }

            try
            {
                foreach (var proj in allProjectiles)
                {
                    proj.Stop();
                }
            }
            catch (Exception e)
            {
                Debug.LogError("Unload Projs Exception: " + e.ToString());
            }
            finally
            {
                allProjectiles.Clear();
            }
            foreach (var kvp in allActors)
            {
                kvp.Value.UnloadResource();
                Destroy(kvp.Value.gameObject);
            }
            allActors.Clear();


            currentBehaviours.Clear();
            behaviourQueue.Clear();
            doneBehaviours.Clear();

            UnloadBase();

            if (BattleManager.Instance.battlePanel.GetComponent<Battle.BattleHeroItemGudieCtrl>() != null)
            {
                Destroy(BattleManager.Instance.battlePanel.GetComponent<Battle.BattleHeroItemGudieCtrl>());
            }
        }

        public void Clear()
        {
            foreach (var buf in allBuffs)
            {
                buf.Value.Stop();
            }
            allBuffs.Clear();
            foreach (var proj in allProjectiles)
            {
                Destroy(proj.gameObject);
            }
            allProjectiles.Clear();

            foreach (var kvp in allActors)
            {
                Destroy(kvp.Value.gameObject);
            }
            allActors.Clear();
            if (IsBattleBaseLoaded)
            {
                UnloadBase();
            }
        }

        #region Load Shadow
        public void LoadShadow()
        {
            RenderManager.GetInstance().ShowShadow();
        }

        public void UnloadShadow()
        {
            RenderManager.GetInstance().HideShadow();
        }
        #endregion

        #region Load BuffTransform
        protected void LoadBuffTransform() {
            AssetLoad.ResourceManager.Instance.LoadResource(@"UIcomponent/BuffTransform");
        }
        protected void UnloadBuffTransform() {
            AssetLoad.ResourceManager.Instance.UnloadResource(@"UIcomponent/BuffTransform");
        }
        #endregion

        #endregion

        public bool IsUltimateShowing() {
            var runningState = GetCurrentState() as BattleRunningState;
            if (runningState != null) {
                if (runningState.GetCurrentStateType() == BattleRoundState.UltimateState || runningState.GetCurrentStateType() == BattleRoundState.UltimateBeforeState) {
                    return true;
                }
            }
            return false;
        }


        #region Particle
        public void PlayParticle(ParticleData data, Vector3 position, bool isAuto)
        {
            var obj = ParticleFactory.CreateParticleObj(data);
            obj.Play(LayerName.SceneParticle, transform, position, 1f, Vector3.one, isAuto);
        }
        #endregion

        #region WordTip

        public void ShowWordTip(OnceHpExport hpExport)
        {
            var bc = GetBattleCharacter(hpExport.targetUid);
            switch (hpExport.specialType)
            {
                case HpInfSpecialType.Crit:
                    ShowWordTip(bc, Mathf.FloorToInt((float)hpExport.infValue), WordType.CRIT);
                    break;
                case HpInfSpecialType.Miss:
                    ShowWordTip(bc, 0, WordType.MISS);
                    break;
                case HpInfSpecialType.Immune:
                    ShowWordTip(bc, 0, WordType.IMMUNE);
                    break;
                default:
                    if (hpExport.infValue > 0)
                    {
                        ShowWordTip(bc, Mathf.FloorToInt((float)hpExport.infValue), WordType.ADD);
                    }
                    else if (hpExport.infValue < 0)
                    {
                        ShowWordTip(bc, Mathf.FloorToInt((float)hpExport.infValue), WordType.DAMAGE);
                    }
                    else
                    {

                    }
                    break;
            }
        }
        public void ShowWordTip(BattleCharacter bc, int num, WordType wt)
        {
            var wordTip = BattleFactory.SpawnWordTip();
            Vector3 pos = bc.transform.position;
            pos.y += bc.height + UnityEngine.Random.Range(0.1f, 0.5f);
            pos.x += UnityEngine.Random.Range(-0.4f, 0.4f);
            pos = battleCamera.WorldToScreenPoint(pos);
            pos = UICamera.currentCamera.ScreenToWorldPoint(pos);
            pos.z = 0f;
            var wordTipTrans = wordTip.transform;
            wordTipTrans.SetParent(BattleManager.Instance.battlePanel.wordAnchor);
            wordTipTrans.transform.localScale = Vector3.one;
            wordTipTrans.transform.localEulerAngles = Vector3.zero;
            wordTipTrans.transform.position = pos;
            if (wt == WordType.ADD)
            {
                wordTip.GetComponent<BloodWordKit>().ShowHeal(num);
            }
            else if (wt == WordType.DAMAGE)
            {
                wordTip.GetComponent<BloodWordKit>().ShowNumber(num);
            }
            else if (wt == WordType.MISS)
            {
                wordTip.GetComponent<BloodWordKit>().ShowMiss();
            }
            else if (wt == WordType.IMMUNE)
            {
                wordTip.GetComponent<BloodWordKit>().ShowImmune();
            }
            else if (wt == WordType.CRIT)
            {
                wordTip.GetComponent<BloodWordKit>().ShowCrit(num);
            }
            BattleFactory.UnSpawn(wordTip, 1.3f);
        }
        #endregion

        #region DropBall

        public void AddDropFireBall(BattleCharacter bc, BattleCharacter targetBC)
        {
            var ball = BattleFactory.SpawnDropFireBall();
            ball.GetComponent<DropFireBall>().Drop(bc, targetBC);
            OnDropFireBall(ball);
        }

        public void AddDropWaterBall(BattleCharacter bc) {
            var ball = BattleFactory.SpawnDropBall();
            ball.GetComponent<DropAngerBall>().Drop(bc);
            OnDropAngerBall(ball);
        }

        public void AddDropWaterBall(BattleCharacter source, BattleCharacter target)
        {
            var ball = BattleFactory.SpawnDropBall();
            ball.GetComponent<DropAngerBall>().Drop(source, target);
            OnDropAngerBall(ball);
        }

        public virtual void OnDropFireBall(GameObject ball)
        {

        }

        public virtual void OnDropAngerBall(GameObject ball)
        {

        }


        #endregion


        public virtual bool isSkillCanCast4UI(BattleCharacter bc, SkillType st) {
            return bc.IsSkillCanCast4UI(st);
        }

        public bool IsCanTransfer()
        {
            if (isFormationTransfered)
            {
                return false;
            }
            if (hasRoundFight)
            {
                return false;
            }
            return true;
        }

        public void UltimateImmediateRespectCallBack(ResponseBase rb)
        {
            var attackPackage = rb.packageData as OnceAttackPackage;
            if (attackPackage == null)
            {
                Debug.LogError("[UltimateImmediateRespectCallBack] Package Not OnceAttackPackage. Type:" + rb.packageData.GetType().ToString());
                return;
            }
            attackPackage.ImmediateDeal();
        }

        public virtual void Init(BattleManager m)
        {
            manager = m;
            allActors = new Dictionary<string, BattleCharacter>();
            allProjectiles = new List<Projectile>();
            roundNum = 0;
            maxRoundNum = 0;
            attackerAngerNum = 0;
            defenderAngerNum = 0;
            initRunCamp = BattleCamp.Attacker;
            IsSceneLoaded = false;
            IsBattleBaseLoaded = false;
            isCanRoundOver = false;
            isCanBattleOver = false;
            isFormationTransfered = false;
            roundBeginExports = new List<OnceAttackPackageBase>();
            roundEndExports = new List<OnceAttackPackageBase>();
            isPlayedWinAudio = false;
            isComboGuideNeed = false;
            SetIsHaveAutoFightTimeout(true);


            InitState();
        }
        public override void InitState()
        {
            allStates = new Dictionary<BattleState, Fsm.FsmStateMonoBase<BattleState>>();
            allStates[BattleState.ResourceLoadState] = new ResourceLoadState(BattleState.ResourceLoadState, this);
            allStates[BattleState.EntranceState] = new EntranceState(BattleState.EntranceState, this);
            allStates[BattleState.BattleRunningState] = new BattleRunningState(BattleState.BattleRunningState, this);
            allStates[BattleState.ResultState] = new ResultState(BattleState.ResultState, this);
        }

        public long tickCount = 0;

        public void Tick()
        {
            tickCount++;
            UpdateState();
            foreach (var bc in allActors)
            {
                bc.Value.Tick();
            }
            List<Projectile> projs = new List<Projectile>();
            foreach (var bp in allProjectiles)
            {
                bp.Tick();
                if (bp.isNeedRecycle)
                {
                    projs.Add(bp);
                }
            }
            foreach (var bp in projs)
            {
                bp.Stop();
            }
            foreach (var buf in allBuffs) {
                buf.Value.Tick();
            }

            CameraController.Instance.Tick();

        }

        #region MessageCtrl
        public void SendMessage(int opcode, object msg)
        {
            var bp = new BattlePacket();
            bp.opcode = opcode;
            bp.kBody = msg;
            BattleMsgCtrl.GetInstance().msgCtrl.ViewSend(bp);
        }
        public void ReceiveMessage(BattlePacket bp)
        {
            if (BattleManager.Instance.isSkip)
            {
                switch (bp.opcode)
                {
                    case OpDefine.SCAttackResult:
                        FightOver(bp.kBody);
                        break;
                }
            }
            else
            {
                switch (bp.opcode)
                {
                    case OpDefine.SCFightStart:
                        FightStart(bp.kBody);
                        break;
                    case OpDefine.SCNewRound:
                        NewRound(bp.kBody);
                        break;
                    case OpDefine.SCCanRoundOver:
                        CanRoundOver(bp.kBody);
                        break;
                    case OpDefine.SCRoundOver:
                        ConfirmRoundOver(bp.kBody);
                        break;
                    case OpDefine.SCTransBegin:
                        TransBegin(bp.kBody);
                        break;
                    case OpDefine.SCTransOver:
                        TransOver(bp.kBody);
                        break;
                    case OpDefine.SCSkillPress:
                        SkillPress(bp.kBody);
                        break;
                    case OpDefine.SCSkillCast:
                        SkillCastBegin(bp.kBody);
                        break;
                    case OpDefine.SCUltimateCastBegin:
                        UltimateCastBegin(bp.kBody);
                        break;
                    case OpDefine.SCTickExport:
                        TickExport(bp.kBody);
                        break;
                    case OpDefine.SCShoot:
                        ShootProj(bp.kBody);
                        break;
                    case OpDefine.SCAttackResult:
                        FightOver(bp.kBody);
                        break;
                    case OpDefine.SCJumpUltimate:
                        JumpUltimate(bp.kBody);
                        break;
                }
            }
        }

        /// <summary>
        /// 战斗开始
        /// </summary>
        /// <param name="o"></param>
        void FightStart(object o)
        {
            SCFightStart msg = o as SCFightStart;
            initRunCamp = (BattleCamp)msg.initRunCamp;
        }
        /// <summary>
        /// 进入新的回合
        /// </summary>
        /// <param name="o"></param>
        void NewRound(object o)
        {
            SCNewRound msg = o as SCNewRound;
            if (msg != null)
            {
                roundNum = msg.roundNum;
                RefreshSideBattleData(msg.attacker, BattleCamp.Attacker);
                RefreshSideBattleData(msg.defender, BattleCamp.Defender);
                foreach (var uid in GetActorUids(curRunningCamp))
                {
                    var bc = GetBattleCharacter(uid);
                    if (bc.isAlive)
                        bc.SwitchState(ActorState.AttackReady);
                    else
                        bc.SwitchState(ActorState.Dead);
                }
                foreach (var uid in GetActorUids(GetOppoCamp(curRunningCamp)))
                {
                    var bc = GetBattleCharacter(uid);
                    if (bc.isAlive)
                        bc.SwitchState(ActorState.Defend);
                    else
                        bc.SwitchState(ActorState.Dead);
                }
                ViewRespectCtrl.GetInstance().NewRound();
                var runningState = GetCurrentState() as BattleRunningState;
                var _state = runningState.GetCurrentState() as RoundEnterState;
                if (_state != null)
                {
                    _state.NewRoundCallback();
                }

            }
        }

        public bool isCanRoundOver = false;
        /// <summary>
        /// 普通技能都放完了，可以回合结束
        /// </summary>
        /// <param name="o"></param>
        void CanRoundOver(object o)
        {
            isCanRoundOver = true;
        }

        /// <summary>
        /// 回合真的结束了
        /// </summary>
        /// <param name="o"></param>
        void ConfirmRoundOver(object o)
        {
            SCRoundOver msg = o as SCRoundOver;
            var runningState = GetCurrentState() as BattleRunningState;
            if (runningState != null)
            {
                runningState.battleRoundCtrl.SwitchState(BattleRoundState.RoundLeaveState);
                var state = runningState.battleRoundCtrl.GetState(BattleRoundState.RoundLeaveState) as RoundLeaveState;
                if (state != null)
                {
                    state.ConfirmRoundLeave((BattleCamp)msg.nextRoundRunCamp);
                }
            }

        }

        void TransBegin(object o)
        {
            // TEMP
            transLeftTime = 15;
            if (currentState == allStates[BattleState.BattleRunningState])
            {
                ((BattleRunningState)currentState).TransBegin();
            }
        }

        public float transLeftTime = 15;

        void TransOver(object o)
        {
            SCTransOver msg = o as SCTransOver;
            if (msg != null)
            {
                for (int i = 0; i < msg.actorUids.Count; ++i)
                {
                    var bc = GetBattleCharacter(msg.actorUids[i]);
                    if (bc != null)
                    {
                        bc.FPId = msg.actorFPIds[i];
                    }
                }

                for (int i = 0; i < msg.actorUids.Count; ++i)
                {
                    var bc = GetBattleCharacter(msg.actorUids[i]);
                    if (bc != null)
                    {
                        bc.SwitchState(ActorState.AttackReady);
                    }
                }
                if (currentState == allStates[BattleState.BattleRunningState])
                {
                    ((BattleRunningState)currentState).TransEnd();
                }
            }
        }

        /// <summary>
        /// 技能进入释放队列
        /// </summary>
        /// <param name="o"></param>
        void SkillPress(object o)
        {
            SCSkillPress msg = o as SCSkillPress;
            if (msg != null)
            {
                var bc = GetBattleCharacter(msg.actorUid);
                var skill = bc.GetSkill((SkillType)msg.skillType);
                if (skill != null)
                {
                    skill.Enqueue();
                    hasRoundFight = true;
                }
            }
        }


        int currentFPId = -1;
        BattleCamp currentFPCamp;
        public bool IsCurrentShowBC(BattleCharacter bc)
        {

            if (bc.FPId == currentFPId && bc.camp == currentFPCamp)
            {
                return true;
            }
            return false;
        }
        public void ResetCurrentShowBC()
        {
            currentFPId = -1;
        }

        /// <summary>
        /// 技能开始释放
        /// </summary>
        /// <param name="o"></param>
        void SkillCastBegin(object o)
        {
            SCSkillCast msg = o as SCSkillCast;
            if (msg != null)
            {
                if (msg.skillType == (int)SkillType.Skill1 || msg.skillType == (int)SkillType.Skill2)
                {
                    EnqueueBehaviour(msg.actorUid, (SkillType)msg.skillType, msg.FPId, (BattleCamp)msg.targetCamp, msg.needMove);
                }
                else
                {
                    Debug.LogError("[SkillCastBegin] Receive Ultimate. Use SCUltimateCastBegin instead!!");
                }
                currentFPId = msg.FPId;
                currentFPCamp = (BattleCamp)msg.targetCamp;
                manager.battlePanel.ReceiveSkillCastMessage(msg.actorUid, (SkillType)msg.skillType);


            }
        }

        string isAttackState()
        {
            var state = GetCurrentState() as BattleRunningState;
            if (state != null)
            {
                return state.GetCurrentStateType().ToString();
            }
            else
            {
                return "null";
            }

        }

        /// <summary>
        /// 大招开始释放
        /// </summary>
        /// <param name="o"></param>
        void UltimateCastBegin(object o)
        {
            SCUltimateCastBegin msg = o as SCUltimateCastBegin;
            if (msg != null)
            {
                var bc = GetBattleCharacter(msg.actorUid);
                if (curRunningCamp != bc.camp)
                {
                    //return;
                }
                EnqueueUltimateBehaviour(msg.actorUid, msg.targetUids);
                manager.battlePanel.ReceiveSkillCastMessage(msg.actorUid, SkillType.Ultimate);
                BattleManager.Instance.battlePanel.UltimateHide();

            }
        }

        void JumpUltimate(object o)
        {
            var state = GetCurrentState() as BattleRunningState;
            if (state != null)
            {
                if (state.GetCurrentStateType() == BattleRoundState.UltimateState)
                {
                    var _state = state.GetCurrentState<UltimateState>();
                    _state.Skip();
                }
                else if (state.GetCurrentStateType() == BattleRoundState.UltimateBeforeState)
                {
                    var _state = state.GetCurrentState<UltimateBeforeState>();
                    _state.Skip();
                }
            }
        }

        /// <summary>
        /// 发射抛射物
        /// </summary>
        /// <param name="o"></param>
        void ShootProj(object o)
        {
            SCShoot msg = o as SCShoot;
            if (msg != null)
            {
                ViewRespectCtrl.GetInstance().AddResponse(msg);
            }
        }

        /// <summary>
        /// 输出
        /// </summary>
        /// <param name="o"></param>
        void TickExport(object o)
        {
            SCTickExport msg = o as SCTickExport;
            if (msg != null)
            {
                if (msg.exportTimer == 0)
                {
                    foreach (var onceCast in msg.tickOnceCasts)
                    {
                        if (onceCast.skillOnceCasts != null)
                        {
                            var attackPackage = new OnceAttackPackage(onceCast.skillOnceCasts);
                            if (IsUltimateShowing())
                            {
                                var runningState = GetCurrentState() as BattleRunningState;
                                runningState.AddOnceAttackCache(attackPackage);
                                attackPackage.UltimateDeal();
                            }
                            else
                            {
                                attackPackage.NormalDeal();
                            }
                        }
                        if (onceCast.buffOnceCasts != null)
                        {
                            var buffPackage = new OnceBuffPackage(onceCast.buffOnceCasts);
                            if (IsUltimateShowing())
                            {
                                var runningState = GetCurrentState() as BattleRunningState;
                                runningState.AddOnceAttackCache(buffPackage);
                                buffPackage.UltimateDeal();
                            }
                            else {
                                buffPackage.NormalDeal();
                            }
                        }
                        if (onceCast.talentOnceCasts != null)
                        {
                            var talentPackage = new OnceTalentPackage(onceCast.talentOnceCasts);
                            if (IsUltimateShowing())
                            {
                                var runningState = GetCurrentState() as BattleRunningState;
                                runningState.AddOnceAttackCache(talentPackage);
                                talentPackage.UltimateDeal();
                            }
                            else
                            {
                                talentPackage.NormalDeal();
                            }
                        }
                    }
                }
                else if (msg.exportTimer == 1)
                {
                    foreach (var onceCast in msg.tickOnceCasts)
                    {
                        if (onceCast.skillOnceCasts != null)
                        {
                            var skillPackage = new OnceAttackPackage(onceCast.skillOnceCasts);
                            roundBeginExports.Add(skillPackage);
                        }
                        if (onceCast.buffOnceCasts != null)
                        {
                            var buffPackage = new OnceBuffPackage(onceCast.buffOnceCasts);
                            roundBeginExports.Add(buffPackage);
                        }
                        if (onceCast.talentOnceCasts != null)
                        {
                            var talentPackage = new OnceTalentPackage(onceCast.talentOnceCasts);
                            roundBeginExports.Add(talentPackage);
                        }
                    }

                }
                else if (msg.exportTimer == 2) {
                    foreach (var onceCast in msg.tickOnceCasts)
                    {
                        if (onceCast.skillOnceCasts != null)
                        {
                            var skillPackage = new OnceAttackPackage(onceCast.skillOnceCasts);
                            roundEndExports.Add(skillPackage);
                        }
                        if (onceCast.buffOnceCasts != null)
                        {
                            var buffPackage = new OnceBuffPackage(onceCast.buffOnceCasts);
                            roundEndExports.Add(buffPackage);
                        }
                        if (onceCast.talentOnceCasts != null)
                        {
                            var talentPackage = new OnceTalentPackage(onceCast.talentOnceCasts);
                            roundEndExports.Add(talentPackage);
                        }
                    }
                }
            }
        }



        public BattleCamp winCamp;
        public bool isCanBattleOver = false;

        void FightOver(object o)
        {
            SCAttackResult msg = o as SCAttackResult;
            winCamp = msg.isSuccess == 0 ? BattleCamp.Defender : BattleCamp.Attacker;
            isCanBattleOver = true;
        }

        public void AskNewRound()
        {
            SendMessage(OpDefine.CSNewRound, new CSNewRound());
        }
        public void AskRoundOver()
        {
            SendMessage(OpDefine.CSRoundOver, new CSRoundOver());
        }

        public void AskTrans()
        {
            SendMessage(OpDefine.CSTransBegin, new CSTransBegin());
        }

        public void AskTransEnd()
        {
            CSTransOver msg = new CSTransOver();
            var uids = GetActorUids(BattleCamp.Attacker);
            for (int i = 0; i < uids.Count; ++i)
            {
                var bc = GetBattleCharacter(uids[i]);
                msg.actorUids.Add(uids[i]);
                msg.actorFPIds.Add(bc.FPId);
            }
            SendMessage(OpDefine.CSTransOver, msg);
        }

        public void AskSkillCast(CSSkillCast msg)
        {
            SendMessage(OpDefine.CSSkillCast, msg);
        }

        public void BeginTrans()
        {
            if (!IsCanTransfer())
            {
                return;
            }
            AskTrans();
        }

        public void SendConfirmTrans()
        {
            AskTransEnd();
            // TODO
            /*
            CSConfirmTrans ct = new CSConfirmTrans();
            var uids = GetActorUids(BattleCamp.Attacker);
            for (int i = 0; i < uids.Count; ++i)
            {
                var bc = GetBattleCharacter(uids[i]);
                ct.heroUids.Add(bc.uniqueId);
                ct.heroFPIds.Add(bc.FPId);
            }
            SendMessage(OpDefine.CSConfirmTrans, ct);
            */
        }

        public void SendTransfer(bool isLeft)
        {
            var state = ((FormationTransferState)((BattleRunningState)currentState).battleRoundCtrl.GetCurrentState());
            if (state.IsMoving())
            {
                return;
            }
            state.Transfer(isLeft ? TransForward.Forward : TransForward.Backward);
        }

        public void SendAuto(bool isAuto)
        {
            manager.battleEngine.SetAuto(Engine.CampType.ATTACK, isAuto);
            this.isAuto = isAuto;
            if (isAuto)
            {
                BattleManager.Instance.battlePanel.EnterAuto();
                BoxManager.CreatePopupTextBox(TableTextBlo.GetText("enter_auto"));
            }
            else
            {
                BattleManager.Instance.SetSpeed(BattleSpeed._1x);
                BattleManager.Instance.battlePanel.LeaveAuto();
                BoxManager.CreatePopupTextBox(TableTextBlo.GetText("leave_auto"));
            }
        }

        public void AskJumpUltimate()
        {
            SendMessage(OpDefine.CSJumpUltimate, new CSJumpUltimate());
        }


        #endregion



        public static BattleViewCtrl GetInstance()
        {
            return BattleManager.Instance.battleViewCtrl;
        }

        public static BattleCamp GetOppoCamp(BattleCamp camp)
        {
            if (camp == BattleCamp.Attacker)
                return BattleCamp.Defender;
            else
                return BattleCamp.Attacker;
        }

        public virtual void OnBattleCharacterEnterAttackWaitState(BattleCharacter bc) {

        }

        public bool isPlayedWinAudio = false;

        public bool isTipGuideNeed = false;
        public bool isComboGuideNeed = false;

        #region 长时间未操作是否自动进入自动战斗
        // 默认true
        bool isHaveAutoFightTimeout = true;
        public bool IsHaveAutoFightTimeout
        {
            get
            {
                return isHaveAutoFightTimeout;
            }
        }
        public void SetIsHaveAutoFightTimeout(bool b)
        {
            isHaveAutoFightTimeout = b;
        }
        #endregion

    }

}